﻿using System;
using System.Collections.Generic;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using IQIGame.Onigao.GamePlay;
using LFloatMath.Collision2D;
using LFloatMath.Math;

namespace IQIGame.Onigao.Logic
{
    public class BattleCollisionController : TController
    {
        private BetterLinkedList<CollisionComponent> collisionComponents = new BetterLinkedList<CollisionComponent>();

        public void Initialize()
        {
        }

        public void AddCollider(CollisionComponent rColComp)
        {
            if (rColComp != null)
            {
                this.collisionComponents.AddLast(rColComp);
            }
        }

        public void RmoveCollider(CollisionComponent rColComp)
        {
            if (rColComp != null)
            {
                this.collisionComponents.Remove(rColComp);
            }
        }


        /// <summary>
        /// 检测碰撞
        /// </summary>
        /// <param name="rColComp"></param>
        /// <param name="rResult"></param>
        /// <returns></returns>
        public bool CheckCollision(CollisionComponent rColComp, Action<List<CollisionComponent>, List<Unit>> rOnHit, ref List<Unit> rTargets, Predicate<CollisionComponent> rCondition)
        {
            var rProxy = rColComp.proxy;
            return this.CheckCollision(rProxy, rOnHit, ref rTargets, rCondition);
        }

        /// <summary>
        /// 检测碰撞
        /// </summary>
        /// <param name="rColComp"></param>
        /// <param name="rResult"></param>
        /// <returns></returns>
        public bool CheckCollision(ColliderProxy rProxy, Action<List<CollisionComponent>, List<Unit>> rOnHit, ref List<Unit> rTargets, Predicate<CollisionComponent> rCondition = null)
        {
            bool bHit = false;
            var rResult = ListPool<CollisionComponent>.Get();
            if (rProxy != null && this.collisionComponents.Count > 0)
            {
                var a = rProxy;
                var rNode = this.collisionComponents.FirstNode;
                while (rNode != null)
                {
                    var b = rNode.value.proxy;
                    bool isCollided = a != b && (rCondition?.Invoke(rNode.value) ?? true) && CollisionHelper.CheckCollision(a.Prefab, a.Transform2D, b.Prefab, b.Transform2D);
                    if (isCollided)
                    {
                        rResult.Add(rNode.value);
                        bHit = true;
                    }

                    rNode = rNode.next;
                }

                rOnHit?.Invoke(rResult, rTargets);
            }

            ListPool<CollisionComponent>.Put(rResult);
            return bHit;
        }

        public bool RayCast(Ray2D ray, out LRaycastHit2D ret, out int hitID, LFloat maxDistance, Unit self)
        {
            ret = new LRaycastHit2D();
            LFloat t = LFloat.one;
            hitID = 0;
            if (this.DoRaycast(ray, out t, out hitID, maxDistance, self))
            {
                ret.point = ray.origin + ray.direction * t;
                ret.distance = t * ray.direction.magnitude;
                ret.colliderId = hitID;
                return true;
            }

            return false;
        }

        private bool DoRaycast(Ray2D ray, out LFloat t, out int hitID, LFloat maxDistance, Unit self)
        {
            t = maxDistance;
            hitID = 0;
            if (this.collisionComponents.Count > 0)
            {
                bool bHasCollider = false;
                var rNode = this.collisionComponents.FirstNode;
                while (rNode != null)
                {
                    if (rNode.value.unit != self)
                    {
                        var rProxy = rNode.value.proxy;
                        var rCircle = rProxy.Prefab.collider as CCircle;
                        var rCirTrans = rProxy.Transform2D;
                        if (rCircle != null &&
                            Utils.TestRayCircle(rCirTrans.pos, rCircle.radius,
                                ray.origin, ray.direction, out var fDistance) &&
                            fDistance <= maxDistance &&
                            t >= fDistance)
                        {
                            t = fDistance;
                            hitID = rNode.value.unit.GUID;
                            bHasCollider = true;
                        }
                    }
                    rNode = rNode.next;
                }
                return bHasCollider;
            }
            return false;
        }


        protected override void OnDispose()
        {
        }
    }
}